package org.budo.mybatis.executor.keygen;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.type.TypeHandler;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.mybatis.executor.keygen.MybatisJdbc3KeyGenerator;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author limingwei2
 */
public class BudoJdbc3KeyGenerator extends MybatisJdbc3KeyGenerator {
    private static final Logger log = Slf4j.getLogger();

    private static final ThreadLocal<List<Map<String, Object>>> _GENERATORED_KEYS = new InheritableThreadLocal<List<Map<String, Object>>>();

    private static void _initThreadLocalList() {
        _GENERATORED_KEYS.set(new ArrayList<Map<String, Object>>());
    }

    private static void _addToThreadLocalList(Map<String, Object> map) {
        List<Map<String, Object>> list = _GENERATORED_KEYS.get();
        if (null == list) {
            log.warn("#36 list=" + list + ", initThreadLocalList");
            _initThreadLocalList();
            list = _GENERATORED_KEYS.get();
        }
        list.add(map);
    }

    private static List<Map<String, Object>> _getThreadLocalListAndRemove() {
        List<Map<String, Object>> list = _GENERATORED_KEYS.get();
        _GENERATORED_KEYS.remove();
        return list;
    }

    @Override
    public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        _initThreadLocalList();

        super.processBefore(executor, ms, stmt, parameter);
    }

    @Override
    public void processAfter(Executor executor, MappedStatement mappedStatement, Statement statement, Object parameter) {
        super.processAfter(executor, mappedStatement, statement, parameter);

        // no, remove after get
    }

    public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) {
        super.processBatch(ms, stmt, parameters);
    }

    protected void populateKeys(ResultSet resultSet, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
        Map<String, Object> map = new HashMap<String, Object>();

        for (int i = 0; i < keyProperties.length; i++) {
            TypeHandler<?> typeHandler = typeHandlers[i];

            if (null != typeHandler) {
                Object value = typeHandler.getResult(resultSet, i + 1);
                metaParam.setValue(keyProperties[i], value);

                map.put(keyProperties[i], value);
            }
        }

        _addToThreadLocalList(map);
    }

    public static Integer getFirstGeneratoredKeyAsInteger() {
        List<Map<String, Object>> list = _getThreadLocalListAndRemove();

        if (null == list || list.size() < 1) {
            return null;
        }

        Map<String, Object> map_0 = list.get(0);
        if (map_0.isEmpty()) {
            return null;
        }

        Object value = MapUtil.firstEntryValue(map_0);
        String valueStr = value.toString();

        return new Integer(valueStr);
    }

    public static Integer[] getGeneratoredKeyAsIntegerArray() {
        List<Map<String, Object>> list = _getThreadLocalListAndRemove();

        if (null == list) {
            return null;
        }

        Integer[] generatoredValues = new Integer[list.size()];
        for (int i = 0; i < list.size(); i++) {
            Map<String, Object> map = list.get(i);
            Object value = MapUtil.firstEntryValue(map);
            String valueStr = value.toString();
            generatoredValues[i] = new Integer(valueStr);
        }

        return generatoredValues;
    }
}
